package com.netnovelstore.util;

import com.netnovelstore.dao.BookMapper;
import com.netnovelstore.pojo.BookContent;
import com.netnovelstore.pojo.Exception.NotDataException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.*;
import java.util.*;
import java.util.regex.Pattern;

@Slf4j
@Component
@Data
public class Resolver {
    private String filePath;
    private FileReader fileReader;
    private FileWriter fileWriter;
    private File file;
    private Set<String> novels;  //未处理的小说（不含后缀名）

    private Pattern r = Pattern.compile("^.*第.+章");

    private String charsetName = "GBK";

    private BookMapper bookMapper;

    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    public Resolver(BookMapper bookMapper, SqlSessionFactory sqlSessionFactory) {
        this.bookMapper = bookMapper;
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Async("taskExecutor")
    public void run(String filePath) throws Exception {
        log.info("即将进行解析小说处理：   " + filePath);
        setFilePath(filePath);
        init();
        log.info("解析完成");
    }


    /**
     * @return void
     * @Author maotao
     * @Description 分解小说并存入数据库
     * @Date 14:30 2023/5/10
     * @Param []
     **/
//    @Async("taskExecutor")
    public List<BookContent> ProcessNovelByBookNumber(Integer bookNumber, String bookPath) throws IOException {
        log.info("即将进行[" + bookNumber + "]解析小说处理：   " + filePath);
        LinkedList<StringBuilder> ArticleList = ReaderAndSolve(bookPath);
        List<BookContent> bookContentLinkedList = new ArrayList<>(ArticleList.size());
        int count = 1;
        for (StringBuilder sb : ArticleList) {
            BookContent bookContent = new BookContent();
            bookContent.setBookContent(sb.toString()).setBookNumber(bookNumber).setBookContentNumber(count);
            bookContentLinkedList.add(bookContent);
            count++;
        }
        return bookContentLinkedList;
    }

    /**
     * @return void
     * @Author maotao
     * @Description 初始化解析器
     * @Date 13:01 2022/10/5
     * @Param []
     **/
    private void init() throws Exception {
        if (filePath == null || filePath.equals(""))
            throw new NotDataException("filePath is null");
        file = new File(filePath);

        novels = getFileName_novel();
        process();
    }

    /**
     * @return java.util.Set<java.lang.String>
     * @Author maotao
     * @Description 标识未分解的txt
     * @Date 13:01 2022/10/5
     * @Param []
     **/
    private Set<String> getFileName_novel() {
        File[] files = file.listFiles();
        Set<String> set = new HashSet<>(files.length);
        for (File f : files) {
            //文件
            if (f.isFile()) {
                String name = f.getName();
                set.add(name.substring(0, name.length() - 4));
            }
        }
        for (File f : files) {
            //文件夹
            if (f.isDirectory()) {
                set.remove(f.getName());
            }
        }
        return set;
    }

    /**
     * @return void
     * @Author maotao
     * @Description 对未解析的txt进行解析整合
     * @Date 13:01 2022/10/5
     * @Param []
     **/
    private void process() throws Exception {
        if (novels == null)
            throw new Exception("novels is null");
        Object[] array = novels.toArray();
        for (Object name : array) {
            //创建文件夹
            new File(filePath + "/" + name.toString()).mkdir();
            //对txt进行分解
            LinkedList<StringBuilder> ArticleList = ReaderAndSolve(name.toString());
            WriteArticleList(ArticleList, name.toString());
        }
    }

    /**
     * @return java.util.LinkedList<java.lang.StringBuilder>
     * @Author maotao
     * @Description 解析txt
     * @Date 13:04 2022/10/5
     * @Param [targetName]
     **/
    private LinkedList<StringBuilder> ReaderAndSolve(String targetName) throws IOException {

        //分解目标的txt源
        String targetFileName = targetName + ".txt";
        log.info("reader -> " + filePath + "/" + targetFileName);
        if (FileCodingTool.isUtf8(new File(filePath + "/" + targetFileName))) {
            this.charsetName="UTF-8";
        }else{
            this.charsetName = "GBK";
        }
        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath + "/" + targetFileName), charsetName);
        BufferedReader in = new BufferedReader(isr);
        StringBuilder content = new StringBuilder(10000);
        LinkedList<StringBuilder> ArticleList = new LinkedList<>();
        while (in.ready()) {
            String line = in.readLine();
            if (r.matcher(line).find()) {
                content.append(line);
                content.append("<br>");
                break;
            }
        }
        while (in.ready()) {
            String line = in.readLine();
            if (r.matcher(line).find()) {
                ArticleList.add(content);
                content = new StringBuilder(10000);
            }
            content.append(line);
            content.append("<br>");
        }
        ArticleList.add(content);
        in.close();
        return ArticleList;
    }

    /**
     * @return void
     * @Author maotao
     * @Description 写入到磁盘
     * @Date 13:04 2022/10/5
     * @Param [ArticleList, targetName]
     **/
    private void WriteArticleList(LinkedList<StringBuilder> ArticleList, String targetName) throws IOException {
        //分解后的txt要存的路径
        String path = filePath + "/" + targetName + "/";
        int cnt = 1;
        for (StringBuilder content : ArticleList) {
            //创建txt文件
            String targetPath = String.format("%s/%d.txt", path, cnt++);
            File txt = new File(targetPath);
            if (txt.exists())
                continue;
            fileWriter = new FileWriter(txt.getPath());
            BufferedWriter out = new BufferedWriter(fileWriter);
            out.write(content.toString());
            out.close();
        }

    }


}
